/*
 * Exception.h
 *
 *  Created on: 2009/01/18
 *      Author: elecgon
 */

#ifndef EXCEPTION_H_
#define EXCEPTION_H_

#include <exception>

#ifndef PREREQUISITES_H_
#include "core/Prerequisites.h"
#endif


class Exception : public exception
{
public:
    enum ExceptionCodes
    {
    	ERR_MEMORY,
    	ERR_FILE,
    	ERR_IO,
    	ERR_RENDERING_API,
    	ERR_AUDIO_API,
    	ERR_RUNTIME
    };

public:
	Exception(int32 number, const TCHAR* type, const char* file, int32 line)
	{
        mLine = line;
        mNumber = number;
		mTypeName = type;
        mDescription =  _T("");
        mCode = 0;

#ifdef UNICODE
		TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
		::MultiByteToWideChar(CP_UTF8, 0, file, (int32)strlen(file), wstr, 512);
		mFile = wstr;
#else
		mFile = file;
#endif
	}

	Exception(int32 number, const String& description, const int32 code, const TCHAR* type)
    {
        mLine = 0;
        mNumber = number;
		mTypeName =  type;
        mDescription = description;
        mCode =  code;
		mFile =  _T("");
    }

	Exception(int32 number, const String& description, const int32 code, const TCHAR* type, const char* file, int32 line )
	{
        mLine = line;
        mNumber = number;
		mTypeName = type;
        mDescription = description;
        mCode = code;

#ifdef UNICODE
		TCHAR wstr[512]; memset(wstr, 0, sizeof(wstr));
		::MultiByteToWideChar(CP_UTF8, 0, file, (int32)strlen(file), wstr, 512);
		mFile = wstr;
#else
		mFile = file;
#endif
	}

    Exception(const Exception& rhs)
	{
    	mLine = rhs.mLine;
    	mNumber = rhs.mNumber;
    	mDescription = rhs.mDescription;
    	mCode = rhs.mCode;
    	mFile = rhs.mFile;
    	mTypeName = rhs.mTypeName;
    }

	virtual ~Exception() throw() {};

    /** Assignment operator.
    */
    void operator = (const Exception& rhs)
    {
        mDescription = rhs.mDescription;
        mNumber = rhs.mNumber;
        mFile = rhs.mFile;
        mLine = rhs.mLine;
        mCode = rhs.mCode;
		mTypeName = rhs.mTypeName;
    }

public:
	const int32 getLine() const { return mLine; }
	const int32 getNumber() const throw();
	const int32 getCode() const { return mCode; }
	const String& getTypeName() const { return mTypeName; }
	const String& getDescription() const { return mDescription; }
	const String& getFile() const { return mFile; }
	const String& getFullDesc() const { return mFullDesc; }

private:
    int32 			mLine;
    int32 			mNumber;
    int32			mCode;
	String 			mTypeName;
    String 			mDescription;
	String			mFile;
	mutable String	mFullDesc;

};

template <int32 num>
struct ExceptionCodeType
{
	enum { number = num };
};


class MemoryException : public Exception
{
public:
	MemoryException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("MemoryException"), file, line) {}

	MemoryException(int number, const char* file, long line)
		: Exception(number, _T("MemoryException"), file, line) {}
};

class IOException : public Exception
{
public:
	IOException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("IOException"), file, line) {}

	IOException(int number, const char* file, long line)
		: Exception(number, _T("IOException"), file, line) {}
};

class FileException : public Exception
{
public:
	FileException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("FileException"), file, line) {}

	FileException(int number, const char* file, long line)
		: Exception(number, _T("FileException"), file, line) {}
};

class RenderingAPIException : public Exception
{
public:
	RenderingAPIException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("RenderingAPIException"), file, line) {}

	RenderingAPIException(int number, const char* file, long line)
		: Exception(number, _T("RenderingAPIException"), file, line) {}
};

class AudioAPIException : public Exception
{
public:
	AudioAPIException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("RenderingAPIException"), file, line) {}

	AudioAPIException(int number, const char* file, long line)
		: Exception(number, _T("RenderingAPIException"), file, line) {}
};

class RuntimeException : public Exception
{
public:
	RuntimeException(int number, const String& description, const int32 code, const char* file, long line)
		: Exception(number, description, code, _T("RuntimeException"), file, line) {}

	RuntimeException(int number, const char* file, long line)
		: Exception(number, _T("RuntimeException"), file, line) {}
};

/*
 * 	Exception Class Factory
 */
class ExceptionFactory
{
private:
	/// Private constructor, no construction
	ExceptionFactory() {}
public:
	static MemoryException create(
		ExceptionCodeType<Exception::ERR_MEMORY> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return MemoryException(code.number, desc, err, file, line);
	}
	static MemoryException create(
		ExceptionCodeType<Exception::ERR_MEMORY> code,
		const char* file, long line)
	{
		return MemoryException(code.number, file, line);
	}

	static FileException create(
		ExceptionCodeType<Exception::ERR_FILE> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return FileException(code.number, desc, err, file, line);
	}
	static FileException create(
		ExceptionCodeType<Exception::ERR_FILE> code,
		const char* file, long line)
	{
		return FileException(code.number, file, line);
	}

	static IOException create(
		ExceptionCodeType<Exception::ERR_IO> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return IOException(code.number, desc, err, file, line);
	}
	static IOException create(
		ExceptionCodeType<Exception::ERR_IO> code,
		const char* file, long line)
	{
		return IOException(code.number, file, line);
	}

	static RenderingAPIException create(
		ExceptionCodeType<Exception::ERR_RENDERING_API> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return RenderingAPIException(code.number, desc, err, file, line);
	}
	static RenderingAPIException create(
		ExceptionCodeType<Exception::ERR_RENDERING_API> code,
		const char* file, long line)
	{
		return RenderingAPIException(code.number, file, line);
	}

	static AudioAPIException create(
		ExceptionCodeType<Exception::ERR_AUDIO_API> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return AudioAPIException(code.number, desc, err, file, line);
	}
	static AudioAPIException create(
		ExceptionCodeType<Exception::ERR_AUDIO_API> code,
		const char* file, long line)
	{
		return AudioAPIException(code.number, file, line);
	}

	static RuntimeException create(
		ExceptionCodeType<Exception::ERR_RUNTIME> code,
		const String& desc,
		const int32 err, const char* file, long line)
	{
		return RuntimeException(code.number, desc, err, file, line);
	}
	static RuntimeException create(
		ExceptionCodeType<Exception::ERR_RUNTIME> code,
		const char* file, long line)
	{
		return RuntimeException(code.number, file, line);
	}
};

//namespace Alto
//{

#ifndef AltoThrow
#define AltoThrow(num) throw ExceptionFactory::create( \
	ExceptionCodeType<num>(), __FILE__, __LINE__ );
#endif

#ifndef AltoThrowDetail
#define AltoThrowDetail(num, desc, code) throw ExceptionFactory::create( \
	ExceptionCodeType<num>(), desc, code, __FILE__, __LINE__ );
#endif

//}	//end of name space Alto

#endif /* EXCEPTION_H_ */
